package by.alex.dzeshko.facebooksuperclient.image;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.apache.http.client.ClientProtocolException;

import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Message;
import android.support.v4.util.LruCache;
import android.text.TextUtils;
import android.util.Log;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import by.alex.dzeshko.facebooksuperclient.ContextHolder;
import by.alex.dzeshko.facebooksuperclient.R;
import by.alex.dzeshko.facebooksuperclient.async.common.ParamCallback;
import by.alex.dzeshko.facebooksuperclient.http.HttpManager;
import by.alex.dzeshko.facebooksuperclient.image.ImageLoader.Callback;
import by.alex.dzeshko.facebooksuperclient.utils.Md5;

public class ThreadImageLoader {
	
	private static final String LOG_TAG = "ImageThreadLoader";
	
	final int STATUS_ERROR = 0; 
	
	final int STATUS_SUCCESS = 1;
	
	private static ThreadImageLoader instance;
	
	public static ThreadImageLoader getInstance() {
		if (instance == null) {
			instance = new ThreadImageLoader();
		}
		return instance;
	}
	
	Context context = ContextHolder.getInstance().getContext();
	
	final LruCache<String, Bitmap> mMemoryCache;
	
	private List<Callback> mQueue;
	
	private int mNumberOnExecute, counter;
	
	private Handler mHandler;
		
	private ThreadImageLoader() {
		
		final int memClass = ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass();
		final int cacheSize = 1024 * 1024 * memClass / 4;
		//final int cacheSize = 1024*1024*3;
		mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
	        @Override
	        protected int sizeOf(String key, Bitmap bitmap) {
	            return bitmap.getWidth()*bitmap.getHeight()*4;
	        }
	        
	        @Override
			protected void entryRemoved(boolean evicted, String key,
					Bitmap oldValue, Bitmap newValue) {
				if (evicted){
					Log.d("evicted","evicted");
					oldValue.recycle();
					System.gc();
					oldValue = null;
				}
			}
	    };		
	    mQueue = Collections.synchronizedList(new ArrayList<Callback>());
	    mNumberOnExecute = 0;
	    counter = 0;
	}
	
	public void bind(final BaseAdapter adapter, final ImageView imageView, final String url) {
		
		Bitmap bitmap = null;
		bitmap = mMemoryCache.get(url);
		if (bitmap != null) {
			imageView.setImageBitmap(bitmap);
			return;
		} else {
			
				mQueue.add(0, new Callback() {
					
					@Override
					public void onSuccess(Bitmap bm) {
						imageView.setImageBitmap(bm);
						adapter.notifyDataSetChanged();
					}
					
					@Override
					public String getUrl() {
						return url;
					}
	
					@Override
					public void onError(Exception e) {
						//Log.e(LOG_TAG, e.toString());
					}
				});
			
		}
		proceed();
	}
	
	public void bind(final ImageView imageView, final String url, final ParamCallback<Void> paramCallback) {
		
		Bitmap bitmap = null;
		bitmap = mMemoryCache.get(url);
		if (bitmap != null) {
			imageView.setImageBitmap(bitmap);
			paramCallback.onSuccess(null);
			return;
		} else {
			
				mQueue.add(0, new Callback() {
					
					@Override
					public void onSuccess(Bitmap bm) {
						//Log.d(LOG_TAG,"bind onSuccess");
						imageView.setImageBitmap(bm);
						paramCallback.onSuccess(null);
					}
					
					@Override
					public String getUrl() {
						return url;
					}
	
					@Override
					public void onError(Exception e) {
						imageView.setBackgroundResource(android.R.drawable.stat_notify_error);
						paramCallback.onError(e);
						//Log.e(LOG_TAG,"result - error:"+e.getMessage());
					}
				});
		}
		proceed();
	}
	public void bind(final ImageView imageView, final String url) {
		
		Bitmap bitmap = null;
		bitmap = mMemoryCache.get(url);
		if (bitmap != null) {
			imageView.setImageBitmap(bitmap);
			return;
		} else {
				mQueue.add(0, new Callback() {
					
					@Override
					public void onSuccess(Bitmap bm) {
						//Log.d(LOG_TAG,"bind onSuccess");
						imageView.setImageBitmap(bm);
					}
					
					@Override
					public String getUrl() {
						return url;
					}
	
					@Override
					public void onError(Exception e) {
						imageView.setBackgroundResource(android.R.drawable.stat_notify_error);
						//Log.e(LOG_TAG, e.toString());
					}
				});
		}
		proceed();
	}

	private void proceed(){
		if(mNumberOnExecute>20) {
			if(mQueue.size()>2)
				mQueue.remove(mQueue.size()-1);
			return;
		}
		if (mQueue.isEmpty()) {
			return;
		}
		final Callback callback = mQueue.remove(0);
		
		mHandler = new Handler(){

			@Override
			public void handleMessage(Message msg) {
				switch (msg.what) {
				case STATUS_SUCCESS:
					mMemoryCache.put(callback.getUrl(), (Bitmap)msg.obj);
					callback.onSuccess((Bitmap)msg.obj);
					mNumberOnExecute--;
					break;
				case STATUS_ERROR:
					Log.e(LOG_TAG, msg.obj.toString());
					callback.onError((Exception)msg.obj);
					mNumberOnExecute--;
					break;
				default:
					break;
				}
			}
			
		};
		ImageThread imageThread = new ImageThread(callback.getUrl());
		Thread thread = new Thread(imageThread);
		thread.start();
		
	}
	
	class ImageThread implements Runnable{

		private static final String LOG_TAG = "ImageThread";
		
		String url;
		
		Bitmap bitmap;
		
		Message msg;
		
		public ImageThread(String url) {
			this.url = url;
			mNumberOnExecute++;
		}

		@Override
		public void run() {
			
			Log.d(LOG_TAG, "run "+url);
			if(TextUtils.isEmpty(url)){
				msg = mHandler.obtainMessage(STATUS_ERROR, new IllegalArgumentException());
				mHandler.sendMessage(msg);
				return;
			}
			bitmap = null;
			String md5_url = Md5.md5(url);
			File cacheDir = context.getCacheDir();
			File cacheFile = new File(cacheDir, md5_url);
			if(cacheFile.exists()){
				try{
					FileInputStream fis = new FileInputStream(cacheFile);
					bitmap = BitmapFactory.decodeStream(fis);
					fis.close();
				} catch(IOException e){
					Log.e(LOG_TAG,"decode exc: "+e.toString());
				}
				if(bitmap==null) {
					bitmap = loadBitmap(url);
					msg = mHandler.obtainMessage(STATUS_SUCCESS, bitmap);
					mHandler.sendMessage(msg);
					return;
				} else {
					msg = mHandler.obtainMessage(STATUS_SUCCESS, bitmap);
					mHandler.sendMessage(msg);
					return;
				}
			} else{
				bitmap = loadBitmap(url);
				msg = mHandler.obtainMessage(STATUS_SUCCESS, bitmap);
				mHandler.sendMessage(msg);
				return;
			}
		}
	}
		
	private Bitmap loadBitmap(String url) {
		Bitmap bitmap = null;
		try {
			bitmap = HttpManager.getInstance().loadBitmap(url);
		} catch (ClientProtocolException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		if(bitmap!=null)
			putBitmapToDiscCache(bitmap, url);
		return bitmap;
	}
	

	protected void putBitmapToDiscCache(Bitmap bitmap, String url){
		
		File cacheDir = context.getCacheDir();
		File cacheFile = new File(cacheDir, Md5.md5(url));
		FileOutputStream fos = null;
		try {
			fos = new FileOutputStream(cacheFile);
			bitmap.compress(CompressFormat.JPEG, 100, fos);
			fos.flush();
		} catch (FileNotFoundException e) {
			Log.e("ImLoader.PutToDisc",e.toString());
			e.printStackTrace();
		} catch (IOException e) {
			Log.e("ImLoader.PutToDisc",e.toString());
			e.printStackTrace();
		} finally{
			try {
				fos.close();
			} catch (IOException e) {
				// ignored closed or null
				e.printStackTrace();
			}
		}
	}
}
